﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using net.xBei.Models.json;
using xBei.Redis.Extension;

namespace net.xBei.Helper
{
    public static class CacheHelper
    {

        public static ResponseData Cache(this ResponseData data, RedisClient redisClient, string key, int Seconds) {
            redisClient.SetValue(key, data, Seconds);
            return data;
        }
        public static async Task<ResponseData> CacheAsync(this ResponseData data, RedisClient redisClient, string key, int Seconds) {
            await redisClient.SetValueAsync(key, data, Seconds);
            return data;
        }
        public static T Cache<T>(this T data, RedisClient redisClient, string key, int Seconds) where T : class, new() {
            Console.WriteLine($"写入Redis缓存：{key} [{Seconds}]");
            redisClient.SetValue<T>(key, data, Seconds);
            return data;
        }
        public static async Task<T> CacheAsync<T>(this T data, RedisClient redisClient, string key, int Seconds) where T : class, new() {
            Console.WriteLine($"写入Redis缓存：{key} [{Seconds}]");
            await redisClient.SetValueAsync<T>(key, data, Seconds);
            return data;
        }
        public static bool GetCache(this RedisClient redisClient, string redisKey, out ResponseData data) {
            data = null;
            if (redisClient.Exists(redisKey)) {
                data = redisClient.GetObject<ResponseData>(redisKey);
            }
            return data != null;
        }

        public static bool GetCache<T>(this RedisClient redisClient, string redisKey, out T data) where T : class, new() {
            data = null;
            if (redisClient.Exists(redisKey)) {
                data = redisClient.GetObject<T>(redisKey);
            }
            return data != null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisClient"></param>
        /// <param name="redisKey">缓存key</param>
        /// <param name="getRedisCache">从缓存中获取数据</param>
        /// <param name="getData">获取数据的方法</param>
        /// <param name="setRedisCache">保存数据到缓存</param>
        /// <returns></returns>
        public static T GetCache<T>(this RedisClient redisClient, string redisKey, Func<RedisClient, string, T> getRedisCache, Func<T> getData, Action<RedisClient, string, T> setRedisCache) {
            var data = getRedisCache.Invoke(redisClient, redisKey);
            if (data != null) {
                return data;
            }
            data = getData.Invoke();
            setRedisCache.Invoke(redisClient, redisKey, data);
            return data;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="Tout"></typeparam>
        /// <param name="redisClient"></param>
        /// <param name="redisKey"></param>
        /// <param name="getRedisCache">从缓存中获取数据</param>
        /// <param name="getData">获取数据的方法</param>
        /// <param name="action">执行收尾工作</param>
        /// <returns></returns>
        public static Tout GetCache<T, Tout>(this RedisClient redisClient, string redisKey, Func<RedisClient, string, T> getRedisCache, Func<T> getData, Func<T, Tout> action) {
            var data = getRedisCache.Invoke(redisClient, redisKey);
            if (data != null) {
                return action.Invoke(data);
            }
            data = getData.Invoke();
            return action.Invoke(data);
        }
        /// <summary>
        /// 判断Redis中是否有指定（<paramref name="redisKey"/>）的值，有就使用<paramref name="getRedisCache"/>获取，没有的话执行<paramref name="getData"/>获取数据。
        /// 最后把值传给<paramref name="action"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="Tout"></typeparam>
        /// <param name="redisClient"></param>
        /// <param name="redisKey"></param>
        /// <param name="getRedisCache">从缓存中获取数据<typeparamref name="T"/>，获取不到有效数据返回null</param>
        /// <param name="getData">获取数据的方法</param>
        /// <param name="action">执行收尾工作</param>
        /// <param name="setRedisCache">把<paramref name="getData"/>获取的数据写入缓存</param>
        /// <returns></returns>
        public static async Task<Tout> GetCacheAsync<T, Tout>(this RedisClient redisClient,
                                                              string redisKey,
                                                              Func<RedisClient, string, Task<T>> getRedisCache,
                                                              Func<Task<T>> getData,
                                                              Func<T, Tout> action,
                                                              Func<RedisClient, string, T, Task>? setRedisCache = null) {
            var data = await getRedisCache.Invoke(redisClient, redisKey);
            if (data != null) {
                return action.Invoke(data);
            }
            data = await getData.Invoke();
            if (data != null && setRedisCache != null) {
                await setRedisCache.Invoke(redisClient, redisKey, data);
            }
            return action.Invoke(data);
        }

        #region 缓存
        //static T Cache<T>(Action<string> getKey, Action<T> getValue) { 

        //}
        private static ConcurrentDictionary<string, CacheData> _cache = new ConcurrentDictionary<string, CacheData>();
        public static string GetCache(Func<string> getKey, Func<string> getValue) {
            var key = getKey.Invoke();
            if (_cache.TryGetValue(key, out var cacheData) && !cacheData.IsExpired) {
                return cacheData.Value;
            }
            var value = getValue.Invoke();
            _cache.TryAdd(key, new CacheData {
                ExpireTime = DateTime.UtcNow.AddDays(1),
                Value = value,
            });
            return value;
        }
        public static T GetCache<T>(Func<string> getKey, Func<T> getValue) {
            var key = getKey.Invoke();
            if (_cache.TryGetValue(key, out var cacheData) && !cacheData.IsExpired) {
                return cacheData.Value.TryDeserialize<T>();
            }
            var value = getValue.Invoke();
            _cache.TryAdd(key, new CacheData {
                ExpireTime = DateTime.UtcNow.AddDays(1),
                Value = value?.ToJson() ?? string.Empty,
            });
            return value;
        }
        class CacheData
        {
            /// <summary>
            /// 过期时间（UTC）
            /// </summary>
            public DateTime ExpireTime { get; set; }
            public string Value { get; set; } = string.Empty;
            /// <summary>
            /// 是否过期
            /// </summary>
            public bool IsExpired => ExpireTime >= DateTime.UtcNow;
        }
        #endregion
    }
}
